Blueprint Help Send comments on this topic.
Harness Code

Glossary Item Box

Overview

Harness code is called when an event fires in the circuitry that is to be handled by user provided code.  The purpose of the Execute() harness code function is to extract the necessary information from the CLIP circuit and call the appropriate processing function to perform the actual processing required.

In the case of methods, modification of the Execute() function is rarely required as the overload editor within Blueprint generates the correct code for most circumstances.  In threads, the Execute() function must be overridden to implement the specific logic for opening and closing connections as this is not captured in the circuit diagram.  By default, the thread Execute() function calls the default processing function without opening any connections.

For more information on entering harness code, refer to Entering Harness Code.  For more information about the content of a circuit definition, refer to Circuit Definitions.

Examples

In the examples below the 'black' code is auto-generated by the translator, whilst the 'red' code is the additional example code and 'green' to highlight comments.

Execute

The following example shows thread harness code.  The Execute() function executes in a loop (CLIP will relaunch it if it returns TRUE) waiting on some non-CLIP event and then performing its actions.

 

Uns Cct1_ControllerThrdElem::Execute()
{
   Uns failed = FALSE;
      
   // loop until told to stop
   
while ( ! failed )
   {
      failed |= ! Cct1_ControllerThrdBaseElem::Execute();
      
      // get time to sleep for from AST
      this->Aux1_ControlDataAst2Cxn().OpenRead();
      int sleepTime = this->Aux1_ControlDataAst2Rec().Data().Value();
      this->Aux1_ControlDataAst2Cxn().Close();

      // if sleep time is -ve exit
      if ( sleepTime == -1 )
         failed = TRUE;

      else
      {
         // sleep for the time specified in the AST
         ClpSleep( sleepTime );  

         // signal to say we've woke
         this->Aux2_GoCsm1Cxn().Signal();    
      }
   }

   return ! failed;
}

 

For methods, the Execute() function typically just calls the Processing Code via the auto-generated code and need not be edited. However, for threads and call-back functions it is usually the Execute() code that performs the main processing, due to its close involvement with the CLIP event tree.  However, any code that can be isolated from the event tree, should be done in a Process() function for maintainability.

 

Uns Cct1_FFTMthdElem::Execute()
{
   Uns failed = FALSE;

   // open manual connection to Ast
   this->Aux1_ControlDataAst1Cxn().OpenUpdate();

   // base class calls process
   failed |= ! Cct1_FFTMthdBaseElem::Execute();
    
   // if process fails
   //    abort the output
   // else
   //    update the Ast
   //    update state
   // close store

   if ( failed )
      this->Trigger_OutTst1Cxn().Abort();      
   else
   {
      ++this->Aux1_ControlDataAst1Rec().Data().Count();
      ++this->State().Data().Count();

   }
   this->Aux1_ControlDataAst1Cxn().Close();

   return ! failed;
}

 

In the case of call-back functions, the Execute() function calls the associated GUI function (equivalent to a Process() function) with the appropriate data extracted from the trigger cxn.

 

 

Uns Cct1_UpdateDisplayCbfElem::Execute( void* pWnd )
{
   Uns failed = FALSE;
      
   failed |= ! Cct1_UpdateDisplayCbfBaseElem::Execute( pWnd );
      
   // extract data from trigger cxn
   Uns age = this->Trigger_GridTst1Rec(0,0).Data().Age();

   char txt[20];
   sprintf(txt,"Age %d", age);

   // send data to GUI
   Display( pWnd, txt );

   return ! failed;
}

 

Notes 

  • When entering code it is useful (but not essential) to use the 'this' prefix for member functions, as this allows the Visual Studio Intellisense to display a list of the available functions and their format. This simplifies the process of accessing the correct CDL function.
  • When writing harness code the coder needs to distinguish between manual and automatic connections as automatic connections are opened and closed by the CLIP infrastructure whereas manual connections are not.  Hence the coder must open/close manual connections explicitly.
  • When opening a store for write access (whether automatic or manual) the record's Construct() call must called, to allocate memory for the data and to assign it to the record structure.
  • If a manual connection is aborted, the connection must still be closed.
  • When a manual connection is made to a distributed store (or any non-store/semaphore object), the user code needs to wait on that object and not the store. When the object triggers, it's connections are automatically opened (and closed when the connection to the distributor is closed):

Uns Cct1_ControllerThrdElem::Execute()
{
   Uns failed = FALSE;
      
   // loop until told to stop
   
while ( ! failed )
   {
      failed |= ! Cct1_ControllerThrdBaseElem::Execute();
      
      // wait on a distributed store
      this->Aux1_ControlDbx1Cxn().WaitEvent();

      int value = this->Aux1_ControlTst1Rec().Data().Value();

      // do something with the data

      // close the distributor

      this->Aux1_ControlDbx1Cxn().Close();
   }

   return ! failed;
}